//

#ifndef _GAMEVIEW_HPP
#define _GAMEVIEW_HPP

#include "base.hpp"
#include "renderer.hpp"

#include "glm/ext/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

using namespace wtk;

class Scene;


struct SceneLighting
{
  // xyz=color, w=strength
  glm::vec4 ambient{1.f, 1.f, 1.f, 1.f};
  glm::vec3 light_pos{};
  // glm::vec4 diffuse_dir{1.f, 0.f, 0.f, 0.f};
};



class ProgLightSource : public wgl::Program
{
public:
  
  ProgLightSource ()
  {
    wgl::Shader vert_shader{GL_VERTEX_SHADER, std::string{DATADIR} + "/shaders/lightsrc.vert"};
    wgl::Shader frag_shader{GL_FRAGMENT_SHADER, std::string{DATADIR} + "/shaders/lightsrc.frag"};
    attach_shader(vert_shader);
    attach_shader(frag_shader);
    link();
    m_proj.init(*this, "m_proj");
    m_model.init(*this, "m_model");
  }
  
  wgl::UniformSlot m_proj;
  wgl::UniformSlot m_model;
};



class ProgModel : public wgl::Program
{
public:
  
  ProgModel ()
  {
    wgl::Shader vert_shader{GL_VERTEX_SHADER, std::string{DATADIR} + "/shaders/shader.vert"};
    wgl::Shader frag_shader{GL_FRAGMENT_SHADER, std::string{DATADIR} + "/shaders/shader.frag"};
    attach_shader(vert_shader);
    attach_shader(frag_shader);
    link();
    m_proj.init(*this, "m_proj");
    m_model.init(*this, "m_model");
    m_normal.init(*this, "m_normal");
    ambient.init(*this, "scene_lighting.ambient");
    light_pos.init(*this, "scene_lighting.light_pos");
    // diffuse_dir.init(*this, "scene_lighting.diffuse_dir");
  }
  
  wgl::UniformSlot m_proj;
  wgl::UniformSlot m_model;
  wgl::UniformSlot m_normal;
  wgl::UniformSlot ambient;
  wgl::UniformSlot light_pos;
  // wgl::UniformSlot diffuse_dir;
};



class ProgWire : public wgl::Program
{
public:
  
  ProgWire ()
  {
    wgl::Shader vert_shader{GL_VERTEX_SHADER, std::string{DATADIR} + "/shaders/wire.vert"};
    wgl::Shader frag_shader{GL_FRAGMENT_SHADER, std::string{DATADIR} + "/shaders/wire.frag"};
    attach_shader(vert_shader);
    attach_shader(frag_shader);
    link();
    m_proj.init(*this, "m_proj");
  }

  wgl::UniformSlot m_proj;
};



// GameView
//
class GameView :public wtk::Dialog
{
private:
  std::string datadir{DATADIR};

  Scene &scene;
  Renderer renderer;
  
  uint64_t frame_count{0};

  int cam_dist = 15;
  wgl::Camera camera{};
  
  wgl::Mesh axis{};
  wgl::MeshBuffer axis_buffer{};

  wgl::Mesh marker{};
  wgl::MeshBuffer marker_buffer{};
  
  wgl::Mesh suzanne{};
  wgl::MeshBuffer suzanne_buffer{};
  wgl::Transfo suzanne_transfo;

  wgl::Mesh light_model;
  wgl::MeshBuffer light_buffer;
  wgl::Transfo light_transfo;
  
  wgl::Texture texture{};

  ProgLightSource prog_light_source{};
  ProgModel prog_model{};
  ProgWire prog_wire{};

  SceneLighting scene_lighting{};
  
public:
  
  GameView ( wtk::Display &display,
             Scene &scene );

  virtual void event_keyboard ( const Event &event );
  virtual void event_mouse ( const Event &event );
  virtual void draw ( const DrawContext &context );

  void setup_renderer ();
  void render_init ();
  void render ( const DrawContext &context );
};

#endif
